import { Vec3 } from "./Vec3";
import {  ArrayType, EPSILON } from "./common";
/**
 * 四元数
 * @class
 * @author jsbrml
 */
class Quat extends ArrayType {
    /**
     * 构造函数。
     * @constructor
     */
    constructor(element = [0, 0, 0, 1]) {
        super(element);
    }
    /**
     * @property  {Number} x 
     * 【读写】获取四元素的x分量。
     */
    get x() {
        return this[0];
    }
    set x(v) {
        this[0] = v;
    }
    /**
     * @property  {Number} y 
     * 【读写】获取四元素的y分量。
     */
    get y() {
        return this[1];
    }
    set y(v) {
        this[1] = v;
    }
    /**
     * @property  {Number} z
     * 【读写】获取四元素的z分量。
     */
    get z() {
        return this[2];
    }
    set z(v) {
        this[2] = v;
    }
    /**
     * @property  {Number} w 
     * 【读写】获取四元素的w分量。
     */
    get w() {
        return this[3];
    }
    set w(v) {
        this[3] = v;
    }
    /**
     * 转为字符串
     * @method
     */
    toString() {
        return `Quat(${this.join(',')})`
    }
    /**
     * 拷贝运算
     * @method
     */
    copy(v) {
        this[0] = v[0];
        this[1] = v[1];
        this[2] = v[2];
        this[3] = v[3];
        return this;
    }
    /**
     * 克隆
     * @method
     */
    clone() {
        return new Quat(this);
    }
    /**
     * 相等判断
     * @method
     */
    equal(v) {
        return (Math.abs(this[0] - v[0]) <= EPSILON) && (Math.abs(this[1] - v[1]) <= EPSILON) && (Math.abs(this[2] - v[2]) <= EPSILON) && (Math.abs(this[3] - v[3]) <= EPSILON);
    }
    /**
     * 还原为未旋转状态。
     */
    identity() {
        this[0] = 0;
        this[1] = 0;
        this[2] = 0;
        this[3] = 1;
        return this;
    }
    /**
     * 指定角度和弧度构建。
     * @method
     */
    setAxisAngle(axis, rad) {
        rad = rad * 0.5;
        let s = Math.sin(rad);
        this[0] = s * axis[0];
        this[1] = s * axis[1];
        this[2] = s * axis[2];
        this[3] = Math.cos(rad);
        return this;
    }
    /**
     * 获取轴角
     * @method
     */
    getAxisAngle() {
        let axis = new Vec3();
        let rad = Math.acos(this[3]) * 2.0;
        let s = Math.sin(rad / 2.0);
        if (s > EPSILON) {
            axis[0] = this[0] / s;
            axis[1] = this[1] / s;
            axis[2] = this[2] / s;
        } else {
            // If s is zero, return any axis (no rotation - axis does not matter)
            axis[0] = 1;
            axis[1] = 0;
            axis[2] = 0;
        }
        return { axis, rad };
    }
    /**
     * 获取角度
     * @method
     */
    getAngle(q) {
        let dotproduct = this.dot(q);
        return Math.acos(2 * dotproduct * dotproduct - 1);
    }
    /**
     * 四元素相乘
     * @method
     */
    multiply(v) {
        const ax = this[0];
        const ay = this[1];
        const az = this[2];
        const aw = this[3];
        const bx = v[0];
        const by = v[1];
        const bz = v[2];
        const bw = v[3];
        this[0] = ax * bw + aw * bx + ay * bz - az * by;
        this[1] = ay * bw + aw * by + az * bx - ax * bz;
        this[2] = az * bw + aw * bz + ax * by - ay * bx;
        this[3] = aw * bw - ax * bx - ay * by - az * bz;
        return this;
    }
    /**
     * 左乘
     * @method
     */
    premultiply(v) {
        const ax = v[0];
        const ay = v[1];
        const az = v[2];
        const aw = v[3];
        const bx = this[0];
        const by = this[1];
        const bz = this[2];
        const bw = this[3];
        this[0] = ax * bw + aw * bx + ay * bz - az * by;
        this[1] = ay * bw + aw * by + az * bx - ax * bz;
        this[2] = az * bw + aw * bz + ax * by - ay * bx;
        this[3] = aw * bw - ax * bx - ay * by - az * bz;
        return this;
    }
    /**
     * 四元素绕X轴旋转
     * @method
     */
    rotateX(rad) {
        rad *= 0.5;
        const ax = this[0];
        const ay = this[1];
        const az = this[2];
        const aw = this[3];
        const bx = Math.sin(rad);
        const bw = Math.cos(rad);
        this[0] = ax * bw + aw * bx;
        this[1] = ay * bw + az * bx;
        this[2] = az * bw - ay * bx;
        this[3] = aw * bw - ax * bx;
        return this;
    }
    /**
     * 四元素绕Y轴旋转
     * @method
     */
    rotateY(rad) {
        rad *= 0.5;
        const ax = this[0];
        const ay = this[1];
        const az = this[2];
        const aw = this[3];
        const by = Math.sin(rad);
        const bw = Math.cos(rad);
        this[0] = ax * bw - az * by;
        this[1] = ay * bw + aw * by;
        this[2] = az * bw + ax * by;
        this[3] = aw * bw - ay * by;
        return this;
    }
    /**
     * 四元素绕Z轴旋转
     * @method
     */
    rotateZ(rad) {
        rad *= 0.5;
        const ax = this[0];
        const ay = this[1];
        const az = this[2];
        const aw = this[3];
        const bz = Math.sin(rad);
        const bw = Math.cos(rad);
        this[0] = ax * bw + ay * bz;
        this[1] = ay * bw - ax * bz;
        this[2] = az * bw + aw * bz;
        this[3] = aw * bw - az * bz;
        return this;
    }
    /**
     * 计算w
     * @method
     */
    calculateW() {
        let x = this[0],
            y = this[1],
            z = this[2];
        this[0] = x;
        this[1] = y;
        this[2] = z;
        this[3] = Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z));
        return this;
    }
    /**
     * 四元素插值
     * @method
     */
    slerp(q, t) {
        let ax = this[0],
            ay = this[1],
            az = this[2],
            aw = this[3];
        let bx = q[0],
            by = q[1],
            bz = q[2],
            bw = q[3];

        let omega, cosom, sinom, scale0, scale1;

        // calc cosine
        cosom = ax * bx + ay * by + az * bz + aw * bw;
        // adjust signs (if necessary)
        if (cosom < 0.0) {
            cosom = -cosom;
            bx = -bx;
            by = -by;
            bz = -bz;
            bw = -bw;
        }
        // calculate coefficients
        if (1.0 - cosom > EPSILON) {
            // standard case (slerp)
            omega = Math.acos(cosom);
            sinom = Math.sin(omega);
            scale0 = Math.sin((1.0 - t) * omega) / sinom;
            scale1 = Math.sin(t * omega) / sinom;
        } else {
            // "from" and "to" quaternions are very close
            //  ... so we can do a linear interpolation
            scale0 = 1.0 - t;
            scale1 = t;
        }
        // calculate final values
        this[0] = scale0 * ax + scale1 * bx;
        this[1] = scale0 * ay + scale1 * by;
        this[2] = scale0 * az + scale1 * bz;
        this[3] = scale0 * aw + scale1 * bw;
        return this;
    }
    /**
     * 四元素逆
     * @method
     */
    invert() {
        const a0 = this[0];
        const a1 = this[1];
        const a2 = this[2];
        const a3 = this[3];
        const dot = a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3;
        const invDot = dot ? 1 / dot : 0;
        this[0] = -a0 * invDot;
        this[1] = -a1 * invDot;
        this[2] = -a2 * invDot;
        this[3] = a3 * invDot;
        return this;
    }
    /**
     * 从3阶矩阵构建
     * @method
     */
    fromMat3(m) {
        // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
        // article "Quaternion Calculus and Fast Animation".
        let fTrace = m[0] + m[4] + m[8];
        let fRoot;

        if (fTrace > 0.0) {
            // |w| > 1/2, may as well choose w > 1/2
            fRoot = Math.sqrt(fTrace + 1.0); // 2w
            this[3] = 0.5 * fRoot;
            fRoot = 0.5 / fRoot; // 1/(4w)
            this[0] = (m[5] - m[7]) * fRoot;
            this[1] = (m[6] - m[2]) * fRoot;
            this[2] = (m[1] - m[3]) * fRoot;
        } else {
            // |w| <= 1/2
            let i = 0;
            if (m[4] > m[0]) i = 1;
            if (m[8] > m[i * 3 + i]) i = 2;
            let j = (i + 1) % 3;
            let k = (i + 2) % 3;

            fRoot = Math.sqrt(m[i * 3 + i] - m[j * 3 + j] - m[k * 3 + k] + 1.0);
            this[i] = 0.5 * fRoot;
            fRoot = 0.5 / fRoot;
            this[3] = (m[j * 3 + k] - m[k * 3 + j]) * fRoot;
            this[j] = (m[j * 3 + i] + m[i * 3 + j]) * fRoot;
            this[k] = (m[k * 3 + i] + m[i * 3 + k]) * fRoot;
        }
        return this;
    }
    /**
     * 从欧拉角构建
     * @method
     */
    fromEuler(euler) {
        let x = euler[0] /2;
        let y = euler[1] /2;
        let z = euler[2] /2;
        let sx = Math.sin(x);
        let cx = Math.cos(x);
        let sy = Math.sin(y);
        let cy = Math.cos(y);
        let sz = Math.sin(z);
        let cz = Math.cos(z);

        switch (euler.order) {
            case "xyz":
                this[0] = sx * cy * cz + cx * sy * sz;
                this[1] = cx * sy * cz - sx * cy * sz;
                this[2] = cx * cy * sz + sx * sy * cz;
                this[3] = cx * cy * cz - sx * sy * sz;
                break;

            case "xzy":
                this[0] = sx * cy * cz - cx * sy * sz;
                this[1] = cx * sy * cz - sx * cy * sz;
                this[2] = cx * cy * sz + sx * sy * cz;
                this[3] = cx * cy * cz + sx * sy * sz;
                break;

            case "yxz":
                this[0] = sx * cy * cz + cx * sy * sz;
                this[1] = cx * sy * cz - sx * cy * sz;
                this[2] = cx * cy * sz - sx * sy * cz;
                this[3] = cx * cy * cz + sx * sy * sz;
                break;

            case "yzx":
                this[0] = sx * cy * cz + cx * sy * sz;
                this[1] = cx * sy * cz + sx * cy * sz;
                this[2] = cx * cy * sz - sx * sy * cz;
                this[3] = cx * cy * cz - sx * sy * sz;
                break;

            case "zxy":
                this[0] = sx * cy * cz - cx * sy * sz;
                this[1] = cx * sy * cz + sx * cy * sz;
                this[2] = cx * cy * sz + sx * sy * cz;
                this[3] = cx * cy * cz - sx * sy * sz;
                break;

            case "zyx":
                this[0] = sx * cy * cz - cx * sy * sz;
                this[1] = cx * sy * cz + sx * cy * sz;
                this[2] = cx * cy * sz - sx * sy * cz;
                this[3] = cx * cy * cz + sx * sy * sz;
                break;
            default:
                throw new Error('Unknown angle order ' + order);
        }
        return this;
    }
    /**
     * 计算两个四元素的点积。
     * @method
     */
    dot(v) {
        return this[0] * v[0] + this[1] * v[1] + this[2] * v[2] + this[3] * v[3];
    }
    /**
     * 四元数归一化
     * @method
     */
    normalize() {
        const v0 = this[0];
        const v1 = this[1];
        const v2 = this[2];
        const v3 = this[3];
        const len = Math.sqrt(v0 * v0 + v1 * v1 + v2 * v2 + v3 * v3);
        if (len > 0.00001) {
            this[0] = v0 / len;
            this[1] = v1 / len;
            this[2] = v2 / len;
            this[3] = v3 / len;
        } else {
            this[0] = 0;
            this[1] = 0;
            this[2] = 0;
            this[3] = 0;
        }
        return this;
    }
    /**
     * 给定两个向量a,b，计算a->b的四元素。
     * @method
     */
    fromVector(a, b) {
        let tmpvec3 = new Vec3([1, 0, 0]);
        const xUnitVec3 = new Vec3([1, 0, 0]);
        const yUnitVec3 = new Vec3([0, 1, 0]);
        const dot = a.dot(b);
        if (dot < -0.999999) {
            tmpvec3.cross(a);
            if (tmpvec3.length() < 0.000001) {
                tmpvec3.cross(yUnitVec3, a);
            }
            tmpvec3.normalize();
            this.setAxisAngle(tmpvec3, Math.PI);
            return this;
        } else if (dot > 0.999999) {
            this[0] = 0;
            this[1] = 0;
            this[2] = 0;
            this[3] = 1;
            return this;
        } else {
            tmpvec3.copy(a);
            tmpvec3.cross(b);
            this[0] = tmpvec3[0];
            this[1] = tmpvec3[1];
            this[2] = tmpvec3[2];
            this[3] = 1 + dot;
            this.normalize();
            return this;
        }
    }

}
export { Quat }